import re

from docx.text.paragraph import Paragraph, Run
from docx.table import Table

from .sourcetype import SourceType

from typing import Union

Paragraphs = list[Paragraph]
Element = Union[Paragraph, Table]
Reg = Union[str, list[str]]
TextElement = Union[Paragraph, Run]

from .reg_constants import *

KEYS = ["header", "tailer"]

def get_re_result(p: TextElement, reg_str: Reg) -> list:
    """用来获得TextElement的text中匹配re的regular-expresion的所有结果。
    注意！如果re_str是一个列表，且其中的一个regular-expresion已经匹配，
    那么余下的regular-expresion将不会被执行。

    Parameters
    ==========

    p: TextElement
    reg_str: str of regular-expresion or regular-expresion

    Return
    ======

    list: the matched list.

    """
    if isinstance(reg_str, list):
        for check_str in reg_str:
            answer = re.findall(check_str, p.text)
            if answer:
                return answer
        return []
    else:
        return re.findall(reg_str, p.text)


def has_result(p: TextElement, reg_str: Reg = RE_QUESTION_STRS) -> bool:
    """利用TextElement的text来判断。

    Parameters
    ==========

    p: TextElement
    re_str: str of regular-expresion or regular-expresion

    Return
    ======

    bool: True of False

    """
    if get_re_result(p, reg_str):
        return True
    else:
        return False


def get_result_index(paras: Paragraphs, reg_str: Reg) -> dict:
    """获得paras中任意一个Paragraph对应的text匹配上reg_str的序数。

    Parameters
    ==========

    paras: list, the list of TextElement
    re_str: str of regular-expresion or regular-expresion

    Return
    ======

    dict: the key is the ordinal number of paras, the value is
        the matched strings of reg_str.

    """
    answer = {}
    for i in range(len(paras)):
        result = get_re_result(paras[i], reg_str)
        if result:
            answer[i] = result
    return answer


def get_blocks_paras(
    paras: Paragraphs, re_head_str: Reg, re_tail_str: Reg, **kwargs
) -> list:
    """获得以re_head_str识别的结果作为开头，以re_tail_str识别的结果作为结尾，
    这中间所有的Paragraph对应的序数。

    In the kwargs, you can set the start paras number and the stop paras number.


    Parameters
    ==========

    paras: list, The list of TextElement.
    re_head_str: str, the regular-expresion
    re_tail_str: str, the regular-expresion

    Kwargs
    ======

    start: int, the ordinal number of the paras you want to find first.
    stop: int, the ordinal number of the paras you want to find last.

    Return
    ======

    list: the list of dicts,and the dict has two keys of
        "header","tailer".

    """
    start = kwargs.get("start", 0)
    stop = kwargs.get("stop", len(paras))
    blocks = []

    head_index = get_result_index(paras[start:stop], re_head_str)
    tail_index = get_result_index(paras[start:stop], re_tail_str)
    length = len(head_index)
    if length == len(tail_index):
        head_key = list(head_index.keys())
        tail_key = list(tail_index.keys())
        for i in range(length - 1):
            hk = head_key[i]
            tk = tail_key[i]
            hk_next = head_key[i + 1]
            blocks.append({KEYS[0]: [*range(hk, tk)], KEYS[1]: [*range(tk, hk_next)]})
        blocks.append(
            {
                KEYS[0]: [*range(head_key[length - 1], tail_key[length - 1])],
                KEYS[1]: [*range(tail_key[length - 1], stop - 1)],
            }
        )
    else:
        raise ValueError("The header's number is not equals to the tailer.")

    return blocks


def _get_blocks_parts(
    index: int, paras: Paragraphs, head_strs: Reg, tail_strs: Reg, **kwargs
) -> list:
    """获得get_blocks_paras函数返回的列表中每一个字典的特定部分，
    并返回这些部分组成的list.

    In the kwargs, you can set the start paras number and the stop paras number.


    Parameters
    ==========

    index: int, the part of the dict
    paras: list, the list of TextElement
    re_head_str: str, the regular-expresion
    re_tail_str: str, the regular-expresion

    Kwargs
    ======

    start: int, the ordinal number of the paras you want to find first.
    stop: int, the ordinal number of the paras you want to find last.

    Return
    ======

    list: the list of dicts' indexed parts.


    """

    checks = get_blocks_paras(paras, head_strs, tail_strs, **kwargs)
    answer = []
    for s in checks:
        answer.append(s[KEYS[index]])
    return answer


def get_blocks_header(
    paras: Paragraphs, head_strs: Reg, tail_strs: Reg, **kwargs
) -> list:
    """返回paras中以匹配header_strs的Paragraph.text的序数为开头，
    匹配tailer_strs的Paragraph.text的序数为结尾的list组成的list.

    In the kwargs, you can set the start paras number and the stop paras number.


    Parameters
    ==========

    paras: list, the list of TextElement
    re_head_str: str, the regular-expresion
    re_tail_str: str, the regular-expresion

    Kwargs
    ======

    start: int, the ordinal number of the paras you want to find first.
    stop: int, the ordinal number of the paras you want to find last.

    Return
    ======

    list: the list of dicts' indexed parts.


    """

    return _get_blocks_parts(0, paras, head_strs, tail_strs, **kwargs)


def get_blocks_tailer(
    paras: Paragraphs, head_strs: Reg, tail_strs: Reg, **kwargs
) -> list:
    """返回paras中以匹配tailer_strs的Paragraph.text的序数为开头，
    匹配header_strs的Paragraph.text的序数为结尾的list组成的list.

    In the kwargs, you can set the start paras number and the stop paras number.


    Parameters
    ==========

    paras: list, the list of TextElement
    re_head_str: str, the regular-expresion
    re_tail_str: str, the regular-expresion

    Kwargs
    ======

    start: int, the ordinal number of the paras you want to find first.
    stop: int, the ordinal number of the paras you want to find last.

    Return
    ======

    list: the list of dicts' indexed parts.


    """

    return _get_blocks_parts(1, paras, head_strs, tail_strs, **kwargs)


def get_target_set(paras: Paragraphs, reg_str: Reg) -> set:
    """
    将正则表达式应用于paras里的每一个Paragraph的text，如果应用成功，则在返回的集合里增加一个findall的返回值。

    Parameters
    ==========

    paras: list of TextElement
    reg_str: Reg, the regular-expresion

    Return
    ======

    set: the set of reg_str matched strings.

    """

    result_dict = get_result_index(paras, reg_str)
    answer = set()
    results_values = result_dict.values()
    for l in results_values:
        answer = answer.union(set(l))
    return answer


# 获取题源
def get_resource(p: Paragraph, reg_str: Reg = RE_QUESTION_INFORM_STR) -> str:
    check = get_re_result(p, reg_str)
    if len(check) == 1:
        return check[0]
    else:
        raise ValueError("题源不确定！")
